feat(routes): reference MCP App resourceUri from static config and resolve App templates route-relative (#388) - #418
Conversation
🦋 Changeset detectedLatest commit: 4a83878 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 511ee564fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
commit: |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4590613843
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1eda1aafe2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38cea8d5e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…solve App templates route-relative (#388) Static route config may now reference an App instead of repeating its ui:// literal: `appResourceUri('<app>')` from the new light `agent-bundle/routes` subpath resolves at graph-compile time to the App route's config.resourceUri (AB4826 for an unknown App), and top-level const string-literal identifiers — declared locally or `export const`-ed by a relative sibling module — extract statically; AB4806 names both forms. `_meta.ui.resourceUri` is typed through RouteMeta/RouteUiMeta. App route config.template resolves relative to the route module like its imports; the legacy project-root-relative form stays accepted while unambiguous, and AB4827 names both candidate paths otherwise.
…atch changeset (#388 review) Apps of servers packaged as custom/command/remote or left in an AB4800 conflict are never built, so appResourceUri() must not resolve to them (AB4826 instead). Server-mode decision is shared between reference resolution and server assembly. Changeset follows the pre-1.0 rule (patch for additive changes) and ends with the PR marker.
…only; probe absolute templates (#388 review) A relative appResourceUri() reference strips only .ts/.tsx (or the .js/.jsx spelling of them), so dotted App names keep their dot and a mistyped suffix matches nothing. A coincident (absolute) template candidate is probed for existence so a missing file is AB4827 rather than a later bundler failure.
…y server target (AB4828) (#388 review) RouteUiMeta carries an index signature so the rest of the MCP Apps ui vocabulary (prefersBorder, csp, permissions) still satisfies ToolConfig. Validation adds AB4828: a generated route advertising _meta.ui.resourceUri of an App whose targets are narrower than the server's is rejected, whether the URI came from appResourceUri() or was written as a literal.
…'s own server (#388 review) A generated server registers exactly its own Apps, so a cross-server reference (even in the qualified '<server>/<app>' form) is AB4826 naming the foreign App; references from non-MCP routes are AB4826 too. Routes of servers that are not generated ship no config, so their references are left as authored instead of adding noise beside the mode diagnostic.
8c0ee74 to
4a83878
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Fixes #388.
ui://literal. Two constrained reference forms join the static route-config grammar (packages/agent-bundle/src/routes/config-extract.ts):appResourceUri('<app>')— a compile-time helper exported from the new lightagent-bundle/routessubpath (src/routes/public.ts; 545-byte chunk with no imports, so a route module can import it at run time without pulling the compiler into its generated bundle). The route-graph compiler resolves it after the whole tree is discovered to the target App route'sconfig.resourceUri; accepted references are'<app>'(same server),'<server>/<app>','app:<server>/<app>', or a relative module path. An unknown App is the newAB4826(message lists known App route ids).conststring-literal identifiers — a top-levelconst X = '<literal>'in the route module, orexport const X = '<literal>'from a module reached through a relative import inside the project root (.ts/.tsx/.js-style/index resolution, one hop, parsed never executed). This is the issue'sAPP_RESOURCE_URIfromsrc/constants.tsscenario and stays available at run time (result_meta).AB4806, with messages that now say why the identifier was rejected and a recovery that names both supported forms._meta.ui.resourceUrityping:ToolConfig/ResourceConfig/PromptConfig/AppRouteConfig._metaareRouteMeta(Record<string, unknown> & { ui?: RouteUiMeta }), exported fromagent-bundleandagent-bundle/routes.config.templateis route-file-relative (src/routes/app-template.ts, used byroutes/graph.tsandconfig/normalize.ts), matching how imports resolve. The legacy project-root-relative form is still accepted with no diagnostic while it is the only interpretation naming an existing file. When both interpretations name different existing files, or neither exists, the newAB4827names both candidate paths and the fix. The IR keeps the authored path (digest stays machine-independent); the normalized model carries the resolved absolute file.docs/diagnostics.md(grammar paragraph, template rule,AB4806row, newAB4826/AB4827rows),docs/entry-conventions.md(apps row),docs/framework-mode.md(App route + tool example,_metarow). Changeset.changeset/388-route-config-references.md(minor, new public helper/subpath).origin/mainand open PRs (feat(routes): conventional shared layout module for rendered routes (#312) #396 holdsAB4830–AB4832;AB4826/AB4827taken here).Not done (scope): the issue's minor note about
agent-bundle inspect --routesaborting on diagnostics instead of emitting the partial graph — that requires changing the invalid-inspection result shape/CLI output and is left for a follow-up.examples/mcp-appneeds no change: it uses a config-declared App (configentry/templatecorrectly resolve from the project root, where the config file lives) and readsapp.resourceUrifromagent-bundle/mcp-apps, so nothing duplicates the literal. Its check (pnpm --filter @agent-bundle-example/mcp-app check) passes.Evidence
pnpm typecheck— pass.pnpm lint— 0 errors / 0 warnings (1047 files).pnpm test:unit— 2755 passed, 2 failed:mcp-probe-service"timed-out probe remained blocked on teardown" andnative-claude-contract"fails closed when the candidate plugin…" (5 s timeout). Both are unrelated and environmental: the Claude one hashes the real~/.claude/pluginstree (324 MB / 1432 files here) and fails identically on a pristineorigin/mainworktree; the probe one passed on isolated rerun.pnpm test:route-unit— 36 passed, 1 failed (lifecycle-replay5 s timeout) → passed on isolated rerun.pnpm test:projection— 66 passed.pnpm build && pnpm test:integration:run— 948 passed, 0 failed.generated-route-server.test.ts, "Static route config grammar forbids identifier references, so an MCP App's resourceUri must be duplicated as a literal in every tool's _meta.ui.resourceUri; config.template resolves from the project root #388") passes: an App route with an imported-constresourceUriand a route-relative template, a tool usingappResourceUri('dashboard'), and a resource importing the same const; the built server lists_meta.ui.resourceUriresolved for both and the compiled App HTML comes from the route-relative template.Test plan
tests/route-config-extract.test.ts: same-module const; imported const (.js-style and index specifiers); rejections for package specifier, missing module, outside-project module, missing export, non-literal export, type-only import, default import (allAB4806naming both forms);appResourceUri()placeholder recording (paths + positions) and resolution by bare name, qualified id,app:id, relative path with/without extension; unknown App →AB4826(+ bare-form hint outside MCP routes); helper not imported / wrong specifier / non-string / arity / dynamic argument →AB4806.tests/route-graph.test.ts: cross-route resolution (tool, resource, prompt, cross-server tool) to the App URI incl. digest sensitivity;AB4826+AB4806side by side; template route-relative / legacy root-relative (no diagnostic) / ambiguous (AB4827names both) / missing (AB4827) and digest stability; normalizedmcpApps[].templatefor route-relative and legacy,validate/inspectinvalid on ambiguity.tests/generated-route-server.test.ts: end-to-end build + stdio listing as above.pnpm --filter @agent-bundle-example/mcp-app checkpasses.